package ru.qiwi.security;

import java.io.IOException;

import com.simpay.cashierapptest.DialogScreen;

import network.NetworkThread;
import lib.Core;
import Core.Contants;

public class Security implements Runnable {

	public static Security sec;

	public static Security getIns() {
		if (sec == null) {
			byte[] key = (byte[]) Core.readData(Contants.SECURITY_KEY);
			if (key == null) {
				sec = new Security(null);
			} else {
				sec = new Security(key);
			}
		}

		return sec;

	}

	public Security(byte[] data) {

		try {
			key = RSAKey.restore(data, 0, 512);
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		} catch (NullPointerException npe) {
			Thread th = new Thread(this);
			th.start();
		}

	}

	private RSAKey key;

	public String getSign(Object data) throws InterruptedException {
		byte[] hash;
		if (data instanceof byte[]) {

			hash = MD5.md5((byte[]) data);
		} else if (data instanceof String) {
			hash = MD5.md5((String) data);
		} else {
			throw new IllegalArgumentException();
		}
		byte[] toSign = new byte[key.BYTE_LENGTH - 1];
		toSign[0] = MD5.MD5_BEFORE_PADDING;
		int padSize = toSign.length - 2 - MD5.MD5_DER_HEADER.length
				- hash.length;
		int i = 1;
		for (int j = i; j < padSize + i; j++) {
			toSign[j] = MD5.MD5_PADDING;
		}
		i += padSize;
		toSign[i++] = MD5.MD5_AFTER_PADDING;
		System.arraycopy(MD5.MD5_DER_HEADER, 0, toSign, i,
				MD5.MD5_DER_HEADER.length);
		i += MD5.MD5_DER_HEADER.length;
		System.arraycopy(hash, 0, toSign, i, hash.length);
		return base64encode(key.sign(toSign));
	}

	public synchronized byte[] getKey() {
		try {
			return key.serialize();
		} catch (Exception e) {
			return null;
		}
	}

	public synchronized byte[] getPublicKey() {
		try {
			return key.serializePublic();
		} catch (Exception e) {
			return null;
		}
	}

	public synchronized String getPublicKeyAsBase64() {
		try {
			byte[] template = RSAKey.PUB_KEY_DER_HEADER;
			// ��� ���� ASN1,
			// ������������, ���
			// ������ ����� ���� integer
			final int ASN1_INT_TAG = 2;
			byte[] mod = getPublicKey();
			byte expSize = 1;
			while (RSAKey.EXP_VALUE >> expSize * 8 != 0) {
				expSize++;
			}
			byte[] exp = new byte[expSize + 2];
			exp[0] = ASN1_INT_TAG;
			exp[1] = expSize;
			for (int i = expSize - 1; i >= 0; i--) {
				exp[2 + (expSize - 1 - i)] = (byte) ((RSAKey.EXP_VALUE >> i * 8) & 0xFF);
			}
			byte[] all = new byte[template.length + mod.length + exp.length];
			int i = 0;
			System.arraycopy(template, 0, all, i, template.length);
			i += template.length;
			System.arraycopy(mod, 0, all, i, mod.length);
			i += mod.length;
			System.arraycopy(exp, 0, all, i, exp.length);
			return base64encode(all);
		} catch (Exception e) {
			return null;
		}
	}

	public synchronized void run() {
		System.out.println("Start create key...");
		try {
			key = RSAKey.create(512);
		} catch (InterruptedException ignored) {

		}
		System.out.println("Finish create key");

		Core.writeData(Contants.SECURITY_KEY, Security.getIns().getKey());
		NetworkThread networkThread = new NetworkThread();
		networkThread.start();
	}

	public void stop() {
		BigInteger.active = false;
	}

	private static final char PAD64 = '=';
	private static final char[] BASE64 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G',
			'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
			'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
			'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
			'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6',
			'7', '8', '9', '+', '/' };
	private static final int SIGN = 0x80;

	public static String base64encode(byte[] in) {
		if (in == null) {
			return null;
		}
		int len = in.length;
		if (len == 0) {
			return "";
		}
		int size = (len / 3 + (len % 3 == 0 ? 0 : 1)) * 4;
		char[] out = new char[size];
		int i = 0, j = 0;
		byte k, l, b1, b2, b3, val1, val2, val3;
		while (len > 0) {
			if (len < 3) {
				break;
			}
			b1 = in[i++];
			b2 = in[i++];
			b3 = in[i++];
			l = (byte) (b2 & 0x0f);
			k = (byte) (b1 & 0x03);
			val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
					: (byte) ((b1) >> 2 ^ 0xc0);
			val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
					: (byte) ((b2) >> 4 ^ 0xf0);
			val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
					: (byte) ((b3) >> 6 ^ 0xfc);
			out[j++] = BASE64[val1];
			out[j++] = BASE64[val2 | (k << 4)];
			out[j++] = BASE64[(l << 2) | val3];
			out[j++] = BASE64[b3 & 0x3f];
			len -= 3;
		}
		if (len == 2) {
			b1 = in[i];
			b2 = in[i + 1];
			l = (byte) (b2 & 0x0f);
			k = (byte) (b1 & 0x03);
			val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
					: (byte) ((b1) >> 2 ^ 0xc0);
			val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
					: (byte) ((b2) >> 4 ^ 0xf0);
			out[j++] = BASE64[val1];
			out[j++] = BASE64[val2 | (k << 4)];
			out[j++] = BASE64[(l << 2)];
			out[j++] = PAD64;
		} else if (len == 1) {
			b1 = in[i];
			k = (byte) (b1 & 0x03);
			val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
					: (byte) ((b1) >> 2 ^ 0xc0);
			out[j++] = BASE64[val1];
			out[j++] = BASE64[k << 4];
			out[j++] = PAD64;
			out[j++] = PAD64;
		}
		return new String(out);
	}
}
